home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / d86v311b.arc / XBIOS.8 < prev   
Text File  |  1987-09-24  |  19KB  |  541 lines

  1. ;---------------
  2. ;  XBIOS test driver for BIOS.8
  3. ;---------------
  4.  
  5. ; Copyright (C)1987 Eric Isaacson.  All rights reserved.  Permission to
  6. ; copy and use this module is granted ONLY for machines registered for both
  7. ; the A86 assembler and the D86 debugger.
  8.  
  9. ; XBIOS is a program that exercises the BIOS.8 module.    I am providing it
  10. ; to assist those who wish to assist me in porting my D86 debugger to
  11. ; machines with non-IBM-compatible BIOSes.
  12.  
  13. ; You use this source module as follows:
  14. ;
  15. ;  1. Modify the module BIOS.8 to accommodate your specific machine.  You
  16. ;     should NOT need to modify this module, except possibly to add a
  17. ;     new HELP-key name just before the declcaration HELP_HELP below.
  18. ;
  19. ;  2. Assemble both modules with the command  A86 XBIOS.8 BIOS.8
  20. ;     (Command is provided as MAKX.BAT in this package; just type MAKX.)
  21. ;
  22. ;  3. Run the resulting program XBIOS.COM.  The program will run tests
  23. ;     of the BIOS function calls, and tell you about them.  If everything
  24. ;     looks good, then your BIOS.8 probably works.
  25. ;
  26. ;  4. The XBIOS program ends with an interactive mode, displaying keystrokes.
  27. ;     Please try every key on your keyboard, both with and without any
  28. ;     shift-style keys (ALT, SHIFT, CTRL, SECOND, etc.).  Write down the
  29. ;     codes that result.
  30. ;
  31. ;  5. Send me the codes you wrote down, together with your new BIOS.8.  I'll
  32. ;     incorporate it into D86.    Thanks!
  33.  
  34.  
  35.  
  36. ; This is a "quick-and-dirty" program.  It does not meet my standards for
  37. ; professionally-documented source code (no procedure-level comments).
  38.  
  39.  
  40. JMP MAIN
  41.  
  42. ALTF10_HELP:        ; HELP-key name for the IBM-PC
  43.   DB 'Alt-F10',0
  44. F11_HELP:        ; HELP-key name for the TI-PC
  45.   DB 'F11',0
  46. PF5_HELP:
  47.   DB 'Ctrl-PF5',0
  48. ; If your machine uses a different HELP-KEY, declare its name above this line.
  49.  
  50. HELP_HELP:
  51.   DB 'HELP',0
  52.  
  53.  
  54. ENABLE_PORT  DW ?      ; BIOS specific, used by Wang for a port number
  55. V_FLAG         DB ?      ; debugger variable consulted by IBM BIOS_INIT
  56. WAVY_COUNT   DB ?      ; count of how many wavy messages have been output
  57.  
  58. ; DWB causes the initialization of alternating words and bytes
  59.  
  60. DWB MACRO #RX1L
  61.   DW #X
  62.   DB #AX
  63. #E2#EM
  64.  
  65. SKIP2 MACRO    ; skip over the following 2 opcode bytes
  66.   DB 03D       ; CMP AX,iw opcode will do the job
  67. #EM
  68.  
  69.  
  70. SCREEN_P EQU 08000  ; screen buffer is at 08000
  71.  
  72. ; The following are convenient prefix-abbreviations for returned key-codes.
  73.  
  74. CTRL EQU -040       ; e.g. CTRL'A'  -- WARNING CTRL'a' will be wrong!!
  75. FUNC EQU 59+111     ; e.g. FUNC 1 for the F1 function key
  76. SHIFT_F EQU 84+111  ; e.g. SHIFT_F 3 for the shifted F3 key
  77. CTRL_F EQU 94+111   ; e.g. CTRL_F 10 for the control-F10 key
  78. ALT_F EQU 104+111   ; e.g. ALT_F 4 for the Alt-F4 key
  79. ALT_N EQU 120+111   ; e.g. ALT_N 5 for the Alt-5 key
  80.  
  81. BIOS_CALLS:              ; pointers to BIOS-specific action routines
  82.   VID_COPY    DW MONO_COPY  ; video copy
  83.   VID_ATTR    DW IBM_ATTR   ; set attribute at DI to AL
  84.   VID_FIX    DW IBM_FIX    ; fix video screen
  85.   BIOS_BELL    DW IBM_BELL   ; ring bell
  86.   BIOS_KEY    DW IBM_KEY    ; fetch a keystroke
  87.   BIOS_SAVE     DW IBM_SAVE   ; save user's BIOS state (Sanyo only)
  88.   BIOS_RESTORE    DW IBM_RESTORE; restore the saved BIOS state (Sanyo only)
  89.   VIDEO_SEG    DW ?          ; pointer to physical video display segment
  90.   ATTR_BYTES    DW          ; hardware-dependent video codes
  91.    NORM_ATTR    DB 7          ; code for normal video display
  92.    REV_ATTR    DB 070          ; code for reversed-video display
  93. N_BIOS_CALLS EQU ($-BIOS_CALLS)/2
  94.  
  95. BIOS_INIT   DW IBM_CONFIG
  96.  
  97.  
  98. ; CTRL_JUMPS defines the single-key commands recognized by the debugger.  Each
  99. ;   DW below is an address to be jumped to whenever the following DB byte is
  100. ;   taken as a single-key input by the debugger command processor.
  101.  
  102. CTRL EQU -040
  103.  
  104. CTRL_JUMPS:
  105.   DWB  F1_MSG,        FUNC 1
  106.   DWB  F2_MSG,        FUNC 2
  107.   DWB  F3_MSG,        FUNC 3
  108.   DWB  F4_MSG,        FUNC 4
  109.   DWB  F5_MSG,        FUNC 5
  110.   DWB  F6_MSG,        FUNC 6
  111.   DWB  F7_MSG,        FUNC 7
  112. N_FUNCS EQU ($-CTRL_JUMPS)/3
  113.  
  114. L1:                ; control-key functions start here
  115.   DWB  DOWN_MSG,    80+112
  116.   DWB  PGDN_MSG,    81+112
  117.   DWB  UP_MSG,        72+112
  118.   DWB  PGUP_MSG,    73+112
  119.   DWB  HOME_MSG,    71+112
  120.   DWB  SHF7_MSG, SHIFT_F 7
  121.   DWB  ALTF9_MSG,  ALT_F 9
  122. N_CONTROL_KEYS EQU ($-L1)/3
  123.  
  124.          DW F10_MSG
  125. SWITCH_KEY   DB FUNC 10   ; key code for FUNC 10 (switch screens)
  126.  
  127.          DW HELP_HELP
  128. HELP_KEY     DB ALT_F 10  ; key code for the debugger's HELP key
  129.  
  130.   DWB  0,        0FF     ; terminator for this table
  131.  
  132.  
  133.  
  134.  
  135.  
  136. MAIN:
  137.   CALL SET_MACHINE    ; set SUBDIR_CHAR and BIOS_INIT
  138.   CALL BIOS_INIT    ; initialize our specific BIOS
  139.   MOV DS,ES,SS        ; restore segment registers
  140.   CALL TEST_INIT    ; display the settings produced, prompt for key
  141.   CALL TEST_KEY_BELL    ; test the simple key input and bell functions
  142.   CALL TEST_VIDEO    ; test some simple video displays
  143.   CALL TEST_KEY_CODES    ; enter the interactive key-code display loop
  144.   MOV AX,04C00        ; MS-DOS codes for successful program exit
  145.   INT 33        ; exit back to the operating system
  146.  
  147.  
  148. FIRST_TESTING:
  149.   DB 'Survived BIOS_INIT.',0D,0A
  150.   DB 'Subdirectory character is set to "'
  151. SUBDIR_CHAR  DB '\'
  152.   DB '".',0D,0A
  153.   DB 'Video memory is at segment register value '
  154. VIDEO_DISPLAY:
  155.   DB 'xxxx.',0D,0A
  156.   DB 'The normal attribute byte is '
  157. NORM_DISP:
  158.   DB 'xx.',0D,0A
  159.   DB 'The reverse attribute byte is '
  160. REV_DISP:
  161.   DB 'xx.',0D,0A
  162.   DB 'The HELP key is labelled ',0
  163.  
  164. HELP_MSG     DW ALTF10_HELP
  165.  
  166. POST_HELP:
  167.   DB '.',0D,0A
  168.   DB 'Is this right?',0D,0A,0D,0A
  169.   DB 'First testing BIOS key input and bell-ringing.',0D,0A,0
  170.  
  171. TEST_INIT:        ; make initial display of BIOS variables
  172.   MOV BX,VIDEO_SEG    ; fetch the video segment register value
  173.   MOV DI,VIDEO_DISPLAY    ; point to it position in the display message
  174.   MOV AL,BH        ; fetch the high byte of the value
  175.   CALL HEX_AL        ; output the high byte
  176.   MOV AL,BL        ; fetch the low byte of the value
  177.   CALL HEX_AL        ; output the low byte
  178.   MOV DI,NORM_DISP    ; point to NORM_ATTR value in message
  179.   MOV AL,NORM_ATTR    ; fetch the value
  180.   CALL HEX_AL        ; put the hex value into the message
  181.   MOV DI,REV_DISP    ; point to REV_ATTR value in message
  182.   MOV AL,REV_ATTR    ; fetch the value
  183.   CALL HEX_AL        ; put the hex value into the message
  184.   MOV SI,FIRST_TESTING    ; point to the first message
  185.   CALL MESSAGE        ; output the message
  186.   MOV SI,HELP_MSG    ; point to the name of the HELP key
  187.   CALL MESSAGE        ; output the name
  188.   MOV SI,POST_HELP    ; point to the message after the HELP name
  189.   JMP MESSAGE        ; output the message
  190.  
  191.  
  192.  
  193. VID_TEST_MSG:
  194.   DB 'Now we''ll output directly to video memory.',0D,0A
  195.   DB 'Type any two keys--',0D,0A
  196.   DB 'The screen will go blank after the first keystroke.',0D,0A,0
  197.  
  198. ALPHABET_MSG:
  199.   DB 'Now letters A to X go down the left.',0
  200. H_MSG:
  201.   DB 'Reverse video on the H.',0
  202. AFTER_M_MSG:
  203.   DB 'Reverse video in second character beyond the M.',0
  204.  
  205. H_SPOT EQU  ('H'-'A') * 160
  206. AFTER_M_SPOT EQU ('M'-'A') * 160 + 4
  207.  
  208. TEST_VIDEO:
  209.   MOV SI,VID_TEST_MSG   ; point to the "Now testing video" message
  210.   CALL MESSAGE        ; output the message
  211.   CALL KEY_ONLY     ; take a keystroke
  212.   CALL FIRST_FILL    ; blank the screen
  213.   CALL GET_KEY        ; take another keystroke
  214.   CALL ALPHA_LINES    ; put out the alphabet along the side
  215.   MOV SI,ALPHABET_MSG    ; point to message telling about it
  216.   CALL TOP_LINE     ; output the message
  217.   CALL REFRESH        ; send it all to the video
  218.   CALL GET_KEY        ; take another key
  219.   MOV SI,H_MSG        ; point to cursor-on-H message
  220.   CALL TOP_LINE     ; output the message
  221.   CALL REFRESH        ; refresh the video memory
  222.   MOV ES,VIDEO_SEG    ; point to the video segment
  223.   MOV DI,H_SPOT         ; point to "H" character within that segment
  224.   MOV AL,REV_ATTR    ; load the reverse-attribute byte
  225.   CALL VID_ATTR     ; output the byte to the video segment
  226.   MOV ES,SS        ; restore ES
  227.   CALL GET_KEY        ; take another keystroke
  228.   MOV SI,AFTER_M_MSG    ; point to cursor-after-M message
  229.   CALL TOP_LINE     ; output the message
  230.   CALL REFRESH        ; refresh the video memory
  231.   MOV ES,VIDEO_SEG    ; point to the video segment
  232.   MOV DI,H_SPOT     ; point to the previous cursor position in segment
  233.   MOV AL,NORM_ATTR    ; load normal attribute
  234.   CALL VID_ATTR     ; cancel the previous cursor output
  235.   MOV DI,AFTER_M_SPOT    ; point to the new cursor position
  236.   MOV AL,REV_ATTR    ; load reverse-attribute byte
  237.   CALL VID_ATTR     ; output the new cursor
  238.   MOV ES,SS        ; restore ES
  239. GET_KEY:
  240.   CALL BIOS_RESTORE
  241.   CALL BIOS_KEY
  242.   RET
  243.  
  244.  
  245. PLEASE_TYPE:
  246.   DB 'Please type any key.',0D,0A,0
  247. KEY_ONLY_MSG:
  248.   DB 'The BIOS returned hex code '
  249. KEY_ONLY_HEXCODE:
  250.   DB 'xx.',0D,0A,0
  251.  
  252. KEY_ONLY:
  253.   MOV SI,PLEASE_TYPE      ; point to "Please type any key"
  254.   CALL MESSAGE            ; output "Please type any key"
  255.   MOV DI,KEY_ONLY_HEXCODE ; output goes to the key-value report
  256.   MOV SI,KEY_ONLY_MSG      ; point to start of key-value report
  257.   CALL GET_KEY          ; fetch a keystroke from the BIOS
  258.   CALL HEX_AL          ; place the hex code into the message
  259. MESSAGE:          ; output null-terminated SI-message to standard out
  260.   PUSH BX,CX,DX       ; save registers across call
  261.   MOV DX,SI          ; MS-DOS wants the message pointer in DX
  262. L1:              ; loop here to scan for the terminator
  263.   LODSB           ; fetch a string byte
  264.   TEST AL          ; is it the zero terminator?
  265.   JNZ L1          ; loop if not
  266.   LEA CX,[SI-1]       ; point CX to the terminator byte
  267.   SUB CX,DX          ; compute the number of characters in the message
  268.   MOV AH,040          ; MS-DOS function number for WRITE
  269.   MOV BX,1          ; handle number for standard output is 1
  270.   INT 33          ; call MS-DOS to make the output
  271.   POP DX,CX,BX          ; restore clobbered registers
  272.   RET
  273.  
  274.  
  275.  
  276. NOW_BELL:
  277.   DB 'Now we''re using BIOS to ring the bell...',0D,0A,0
  278. HEAR_IT?:
  279.   DB 'Did you hear it?',0D,0A,0
  280.  
  281. TEST_KEY_BELL:        ; take 4 test keystrokes
  282.   CALL KEY_ONLY     ; take key without bell
  283.   CALL KEY_ONLY     ; take second key, without bell
  284.   CALL KEY_BELL     ; take a key then ring the bell
  285. KEY_BELL:        ; prompt for key, then ring the bell
  286.   CALL KEY_ONLY     ; prompt for and take a keystroke
  287.   MOV SI,NOW_BELL    ; point to bell message
  288.   CALL MESSAGE        ; put out the bell message
  289.   CALL BIOS_BELL    ; ring the bell
  290.   MOV SI,HEAR_IT?    ; point to the post-bell message
  291.   JMP MESSAGE        ; output the post-bell message
  292.  
  293.  
  294.  
  295. ; REFRESH updates the console screen to the desired contents, as indicated by
  296. ;   the SCRBASE buffer.  The buffer is updated to show that the actual screen
  297. ;   contents matches the desired contents.
  298.  
  299. REFRESH:
  300.   PUSH BX,CX,DX,BP,SI,DI,ES,DS ; preserve all registers but AX
  301.   MOV ES,VIDEO_SEG           ; we will copy to the screen-segment
  302.   MOV AH,NORM_ATTR      ; characters will have the normal attribute
  303.   MOV SI,SCREEN_P      ; source pointer is our screen buffer
  304.   SUB DI,DI          ; offset of the screen is zero
  305.   MOV BL,24          ; BL will count down lines
  306.   MOV CH,0          ; CX will always hold byte counts
  307. L1:              ; main loop for actual vs. desired comparison
  308.   MOV CL,80          ; count number of characters in a line
  309.   CALL VID_COPY       ; copy this line to the video buffer
  310.   OR SI,0FF          ; advance SI to the end of this buffer-page
  311.   INC SI          ; bump SI to the beginning of the next page
  312.   DEC BL          ; count down lines
  313.   JNZ L1          ; loop if there are more lines
  314.   POP DS,ES,DI,SI,BP,DX,CX,BX    ; restore all the registers
  315.   RET
  316.  
  317.  
  318.  
  319. ; This section of code defines the message-displays for the control keys.
  320.  
  321. MSG MACRO
  322. #1_MSG:
  323.   DB '#1',0
  324. #EM
  325.  
  326. MSG F1
  327. MSG F2
  328. MSG F3
  329. MSG F4
  330. MSG F5
  331. MSG F6
  332. MSG F7
  333. MSG F10
  334. MSG DOWN
  335. MSG PGDN
  336. MSG PGUP
  337. MSG UP
  338. MSG HOME
  339. MSG SHF7
  340. MSG ALTF9
  341.  
  342. ; HEX_AL outputs to DI-pointed memory the 2-digit hex number representing
  343. ;   the value of AL.
  344.  
  345. BINHEX_AL  MACRO
  346.   ADD AL,090H        ; these 4 lines convert AL into an ASCII hex digit,
  347.   DAA            ;     in an outrageously clever and incomprehensible
  348.   ADC AL,040H        ;     fashion!!!  (I got the 8080 version of this out
  349.   DAA            ;     of an early BYTE magazine.)
  350. #EM
  351.  
  352. HEX_AL:
  353.   AAM 16        ; unpack AL into nibbles AH and AL
  354.   CALL >L3        ; swap nibbles, then output new low nibble
  355. L3:            ; call here to output nibble AH
  356.   XCHG AL,AH        ; swap the nibble into AL
  357.   BINHEX_AL        ; convert AL to a hex digit
  358.   STOSB         ; output the digit
  359.   RET
  360.  
  361.  
  362.  
  363. FIRST_FILL:          ; fill the video screen with its first contents
  364.   MOV DI,SCREEN_P      ; point to our screen image
  365.   MOV CX,(24 BY 0)/2      ; load the number of words in the image buffer
  366.   SUB AX,AX          ; we will zero-fill the buffer
  367.   REP STOSW          ; buffer is initialized to all-zeroes
  368.   MOV AL,' '              ; now load a blank
  369.   CALL FILL_LINES      ; fill the character line-buffers with blanks
  370.   CALL VID_FIX          ; insure that this first screen was actually sent
  371.   CALL REFRESH          ; send our screen buffer to video memory
  372.   RET
  373.  
  374.  
  375.  
  376. ALPHA_LINES:          ; put alphabet A to X down the left side
  377.   MOV DI,SCREEN_P      ; point to the upper left corner
  378.   MOV CX,24          ; load the lines count
  379.   MOV AL,'A'              ; load the first letter
  380. L1:              ; loop here to output each letter
  381.   STOSB           ; output the letter
  382.   INC AX          ; increment to the next letter
  383.   ADD DI,255          ; advance output pointer to the next line
  384.   LOOP L1          ; loop to output the next letter
  385.   RET
  386.  
  387.  
  388.  
  389. FILL_LINES:          ; fill all 24 image-lines with AL
  390.   MOV DI,SCREEN_P      ; point to the start of the image-buffer
  391.   MOV BL,24          ; number of lines in the buffer
  392. L1:              ; loop here for each line
  393.   CALL FILL_LINE      ; fill the line with AL
  394.   DEC BL          ; count down lines
  395.   JNZ L1          ; loop to fill the next line
  396.   RET
  397.  
  398.  
  399.  
  400. FILL_LINE:          ; fill the DI-pointed line with AL, advance DI
  401.   MOV CX,80          ; number of characters in the line
  402.   REP STOSB          ; fill the line
  403.   ADD DI,256-80       ; advance output pointer to the next line buffer
  404.   RET
  405.  
  406.  
  407.  
  408. PRESS_KEY:
  409.   DB '   Press any key',0
  410.  
  411. TOP_LINE:                 ; put SI-message and "press key" on top line
  412.   MOV DI,SCREEN_P+3      ; point to the fourth character of the top line
  413.   CALL COPY_STRING        ; output the caller's SI-pointed message
  414.   MOV SI,PRESS_KEY        ; now point to our "press key" message
  415. COPY_LINE:
  416.   CALL COPY_STRING        ; output the "press key" message
  417.   MOV CX,DI          ; copy the beyond-output pointer to CX
  418.   MOV CL,80          ; advance the pointer to the end of the top line
  419.   SUB CX,DI          ; calculate the number of trailing bytes in the line
  420.   MOV AL,' '              ; load blank
  421.   REP STOSB          ; blank-fill the trailing bytes
  422.   RET
  423.  
  424.  
  425.  
  426. ALL_KEYS:
  427.   DB 'Now try out all possible keystrokes.',0
  428. CAP_Q:
  429.   DB 'Capital Q exits program; capital T trashes the screen.',0
  430. TRASH_MESSAGE:
  431.   DB 'The message is output via MS_DOS.',0D,0A
  432.   DB 'It will roll the screen, thereby trashing the display.',0D,0A
  433.   DB 'Type capital-F to fix the screen if it is trashed.',0D,0A,0
  434. KNOWN_MSG:
  435.   DB 'I recognize that function key, its name is '
  436. KNOWN_TAIL:
  437.   DB '               ',0
  438. UNKNOWN_MSG:
  439.   DB 'That key code is not in my table.  Its value is '
  440. UNKNOWN_HEXCODE:
  441.   DB 'xx',0
  442. DISPLAYING_MSG:
  443.   DB 'Displayable character was typed: the character is "'
  444. DISPLAYING_CHAR DB 'x"',0
  445.  
  446. TEST_KEY_CODES:       ; interactive routine for displaying key codes
  447.   MOV SI,ALL_KEYS      ; point to the main prompting message
  448.   MOV DI,SCREEN_P+3      ; point to the location in our buffer for message
  449.   CALL COPY_LINE      ; output the first line of the message
  450.   MOV SI,CAP_Q          ; point to second line of message
  451.   MOV DI,SCREEN_P+0103      ; point to location in buffer for that message
  452.   CALL COPY_LINE      ; output the second line
  453. L1:              ; loop here for each keystroke
  454.   CALL REFRESH          ; refresh the video screen
  455.   CALL GET_KEY          ; take a keystroke
  456.   CMP AL,'Q'              ; is it the exiting capital-Q?
  457.   JE RET          ; if yes then exit
  458.   CMP AL,'T'              ; is it the TRASH command?
  459.   JE >L6          ; jump if yes
  460.   CMP AL,'F'              ; is it the FIX command?
  461.   JE >L7          ; jump if yes
  462.   MOV BL,AL          ; copy character to BL for WAVY-function
  463.   CMP AL,' '              ; is the character a control character?
  464.   JB >L5          ; jump if yes
  465.   CMP AL,07E          ; is the character above the displaying range?
  466.   JA >L5          ; jump if yes
  467.   MOV DISPLAYING_CHAR,AL  ; character is displayable-- put it into message
  468.   MOV SI,DISPLAYING_MSG   ; point to the displayable message
  469.   JMP >L4          ; jump to propagate the message
  470.  
  471. L5:              ; keystroke is not a displayable character
  472.   MOV DI,CTRL_JUMPS+2      ; point to our table of function keys
  473. L2:              ; loop here to search each entry of function table
  474.   CMP B[DI],0FF       ; is the table exhausted?
  475.   JE >L3          ; jump if yes, key not in table
  476.   SCASB           ; does our key match the table entry?
  477.   LEA DI,[DI+2]       ; advance table pointer in case not
  478.   JNE L2          ; loop if not
  479.   MOV SI,[DI-5]       ; the key matches-- fetch the pointer from that record
  480.   MOV DI,KNOWN_TAIL      ; point to the end of the known-message
  481.   CALL COPY_STRING        ; tack the key's name onto the known-message
  482.   MOV AL,0          ; load terminator
  483.   STOSB           ; null-terminate the known-message
  484.   MOV SI,KNOWN_MSG      ; point to the start of the known-message
  485.   JMP >L4          ; jump to propagate the message
  486.  
  487. L3:              ; keystroke was not found in the function table
  488.   MOV DI,UNKNOWN_HEXCODE  ; point to hex-display part of unknown-message
  489.   CALL HEX_AL          ; output our keycode as hex digits
  490.   MOV SI,UNKNOWN_MSG      ; point to the start of the unknown message
  491. L4:              ; common output point
  492.   CALL WAVY_MESSAGES      ; output the SI-pointed message in a wavy display
  493.   JMP L1          ; loop to take another keystroke
  494.  
  495. L6:              ; capital T is seen
  496.   MOV SI,TRASH_MESSAGE      ; point to our message that trashes the screen
  497.   CALL MESSAGE          ; trash the screen with the message
  498.   CALL BIOS_SAVE
  499.   JMP L1          ; loop to take another keystroke
  500.  
  501. L7:              ; capital F is seen
  502.   CALL VID_FIX          ; fix up the trashed screen
  503.   JMP L1          ; loop to take another keystroke
  504.  
  505.  
  506.  
  507. COPY_STRING:          ; copy SI-pointed string to DI
  508.   LODSB           ; load the first character
  509. L1:              ; loop here for each nonzero character
  510.   STOSB           ; output the character
  511.   LODSB           ; fetch the next character
  512.   TEST AL          ; is it the terminating zero?
  513.   JNZ L1          ; loop if not
  514.   RET
  515.  
  516.  
  517. WAVY   DB  3,4,5,6,7,6,5,4
  518.  
  519. WAVY_MESSAGES:          ; make 20 copies of SI-message in a wavy pattern
  520.   INC WAVY_COUNT      ; increment display count
  521.   MOV DI,SCREEN_P+0404      ; point to the first line of the wavy display
  522.   MOV BH,20          ; load the count of lines to be output
  523. L1:              ; loop here to output each line
  524.   MOV CX,DI          ; fetch the output pointer
  525.   MOV CL,CH          ; low byte tells us a reltive line number
  526.   ADD CL,BL          ; add in character code, just to mix things up
  527.   SUB CL,WAVY_COUNT      ; now a repeat of same key will make waves
  528.   AND CX,7          ; waviness will have a period of 8 lines
  529.   XCHG BX,CX          ; swap the line-index into BX, for addressing
  530.   MOV BL,WAVY[BX]      ; translate line-index into an indetation count
  531.   XCHG CX,BX          ; swap the count back into CX
  532.   MOV AL,' '              ; load blank
  533.   REP STOSB          ; indent the message-- this creates the waviness
  534.   PUSH SI          ; save the message pointer for the next line
  535.   CALL COPY_LINE      ; copy the message to this line, with trailing blanks
  536.   POP SI          ; restore the message pointer
  537.   ADD DI,260-80       ; advance output to the next line
  538.   DEC BH          ; count down lines
  539.   JNZ L1          ; loop to output the next line
  540.   RET
  541.